package manual100;

import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.util.*;
import java.util.Map.Entry;

import zclTool.MergeReadResStr;
import zclTool.MergeReadStr;
import zclTool.StrCmp;

class NameValues implements Comparable {
	String name;
	List<String> values = new ArrayList<String>();
	String value_longstr;

	public String toString() {
		String res = name + "\t" + values.toString();
		return res;
	}

	@Override
	public int compareTo(Object arg0) {
		// TODO Auto-generated method stub
		NameValues nv = (NameValues) arg0;

		return this.value_longstr.length() - nv.value_longstr.length();
	}
}

public class F_Join {

	// focus on some examples like einstein

	static StrCmp sc;
	static final int FN = 9;// feature number

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try {
			MergeReadStr mrs = new MergeReadStr(args[0], args[1], 0, 0);
			MergeReadResStr mrrs;
			sc = new StrCmp(true);

			OutputStreamWriter osw = new OutputStreamWriter(
					new FileOutputStream(args[2]), "utf-8");
			OutputStreamWriter osw_num = new OutputStreamWriter(
					new FileOutputStream(args[3]), "utf-8");
			int ln = 0;
			while ((mrrs = mrs.read()) != null ) {
				if (ln % 100 == 0)
					System.out.println(ln);
				ln++;

				List<String[]> attlist = mrrs.line1_list;
				List<String[]> proplist = mrrs.line2_list;

				String guid = mrrs.key_value;
				String entity = attlist.get(0)[1];

				HashMap<String, NameValues> nvatt = cleanIBAttributes(attlist);
				HashMap<String, NameValues> nvprop = cleanFBProps(proplist);

				List<NameValues> nvattlist = new ArrayList<NameValues>();
				List<NameValues> nvproplist = new ArrayList<NameValues>();
				nvattlist.addAll(nvatt.values());
				nvproplist.addAll(nvprop.values());
				
				int numMatch = compareIblist2Fblist(nvattlist, nvproplist,
						guid, osw);
				osw_num.write(guid + "\t" + entity + "\t" + numMatch + "\n");
				
				// printNVMap(nvatt);
				// printNVMap(nvprop);
				// String entity = attlist.get(0)[1];
				// List<KeyLine> klatts = new ArrayList<KeyLine>();
				// List<KeyLine> klprops = new ArrayList<KeyLine>();
				//
				//				
				//				
				// for (String[] att : attlist) {
				// String attvalue = att[4].toLowerCase();
				// klatts.add(new KeyLine(attvalue, att));
				// }
				//
				// for (String[] prop : proplist) {
				// String propvalue = prop[2].toLowerCase();
				// klprops.add(new KeyLine(propvalue, prop));
				// // sharewordsRatio do not consider NUMBERS, like 23.3
				// // will be removed
				// }
				// Collections.sort(klatts);
				// Collections.sort(klprops);
				// int i = 0, j = 0;
				// // debug(klatts,klprops);
				// while (i < klatts.size() && j < klprops.size()) {
				//
				// KeyLine klatt = klatts.get(i);
				// KeyLine klprop = klprops.get(j);
				// if (klatt.key.equals(klprop.key)) {
				// String[] attline = klatt.line;
				// String[] propline = klprop.line;
				//
				// i++;
				// j++;
				// // System.out.println(attline+"\n"+propline);
				// if (attline[2].contains("Infobox")) {
				// String ib = attline[2].replace("Infobox", "")
				// .trim().replace(" ", "_").toLowerCase();
				// String attributeName = attline[3].toLowerCase();
				// osw.write(attline[0] + "\t" + attline[1] + "\t"
				// + ib + "@" + attributeName + "\t"
				// + attline[4] + "\t" + propline[1] + "\t"
				// + propline[2] + "\n");
				// //
				// System.out.print(attline[0]+"\t"+attline[1]+"\t"+ib+"@"+attributeName+"\t"+attline[4]+"\t"+propline[1]+"\t"+propline[2]+"\n");
				// }
				// } else if (klatt.key.compareTo(klprop.key) > 0) {
				// j++;
				// } else {
				// i++;
				// }
				// }
				//
			}
			mrs.close();
			osw.close();
			osw_num.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static HashMap<String, NameValues> cleanIBAttributes(
			List<String[]> attlines) {
		HashMap<String, NameValues> list = new HashMap<String, NameValues>();
		for (int i = 0; i < attlines.size(); i++) {
			String a[] = attlines.get(i);
			String infobox = a[2];
			if (infobox.contains("Infobox")) {
				infobox = infobox.replace("Infobox", "").trim().replace(" ",
						"_").toLowerCase();
				String attname = a[3].trim().replace(" ", "_");
				String name = infobox + "@" + attname;
				String value = a[4];
				String[] values;
				if (value.contains("<br>") || value.contains("[[")) {
					value = value.replace("[[", "<br>").replace("]]", "<br>");
					values = value.split("<br>");
				} else {
					values = new String[1];
					values[0] = value;
				}
				NameValues nv = list.get(name);

				if (nv == null) {
					nv = new NameValues();
					nv.name = name;
					list.put(name, nv);
				}
				for (String v : values) {
					String toput = v.trim();
					if (toput.length() > 1) {
						nv.values.add(toput);
					}
				}
			}
		}
		getValueLongStr(list);
		return list;
	}

	public static HashMap<String, NameValues> cleanFBProps(
			List<String[]> proplist) {
		HashMap<String, NameValues> list = new HashMap<String, NameValues>();
		for (int i = 0; i < proplist.size(); i++) {
			String a[] = proplist.get(i);
			String name = a[1];
			String value = a[2];
			//if(value.length()>50)continue;
			NameValues nv = list.get(name);
			if (nv == null) {
				nv = new NameValues();
				nv.name = name;
				list.put(name, nv);
			}
			nv.values.add(value.trim());
		}
		getValueLongStr(list);
		return list;
	}

	private static void getValueLongStr(HashMap<String, NameValues> list) {
		Iterator<Entry<String, NameValues>> it = list.entrySet().iterator();

		while (it.hasNext()) {
			Entry<String, NameValues> e = it.next();
			NameValues nv = e.getValue();
			nv.value_longstr = "";
			for (String a : nv.values) {
				nv.value_longstr += a + " ";
			}
			
		}

	}

	private static void printNVMap(HashMap<String, NameValues> list) {
		Iterator<Entry<String, NameValues>> it = list.entrySet().iterator();
		while (it.hasNext()) {
			Entry<String, NameValues> e = it.next();
			System.out.println(e.getValue().toString());
		}
	}

	// * f[] is the features returned
	// * (1) When I am sure all f[] should be zero? When fbv.value is 10 times
	// longer than ibv[
	// *
	// * Based on Words, convert values[] into a long string
	// * f[0]: Best case, exact match: Germany vs Germany --> 0 & 1
	// * f[1]: How many shared words between them? Germany vs Germany, Swissland
	// --> 1
	// * f[2]: ratio, f[1]/[length of the short value] e.g. ABC vs ABCD -->
	// f[3]=1
	// * f[3]: ratio, f[1]/[length of the long value] e.g. ABC vs ABCD -->
	// f[4]=0.75
	// * f[4]: cosine, f[1]/sqrt(L1*L2)
	// *
	// * Based on Items: see each items as a whole; [Germany company, British
	// colleage] [Germany colleage, British comany] --> 0
	// * Do such comparision only when f[2] > 0.33
	// *
	// * f[5]: how many items are the same? (if their shared words > 0.9, then
	// we think they are the same)
	// * f[6]: ratio, f[6]/[length of the short]
	// * f[7]: ratio, f[7]/[length of the long]
	// * f[8]: cosine
	public static double compareIbNv2FbNv(NameValues ibv, NameValues fbv,
			double[] f) {
		if (ibv.name.contains("birth_place")
				&& fbv.name.contains("place_of_birth")) {
			System.out.print("");
		}
		try {
			int[] ablen = new int[2];
			int sharewords = sc.sharewords(ibv.value_longstr,
					fbv.value_longstr, ablen);
			if (sharewords == Math.max(ablen[0], ablen[1])) {
				f[0] = 1;
			}
			f[1] = sharewords;
			f[2] = f[1] * 1.0 / Math.min(ablen[0], ablen[1]);
			f[3] = f[1] * 1.0 / Math.max(ablen[0], ablen[1]);
			f[4] = f[1] * 1.0 / ablen[0] / ablen[1];

			if (f[2] > 0.33) {
				int sameitems = 0;
				int ibvitems = ibv.values.size();
				int fbvitems = fbv.values.size();
				for (int i = 0; i < ibvitems; i++) {
					for (int j = 0; j < fbvitems; j++) {
						String v1 = ibv.values.get(i);
						String v2 = fbv.values.get(j);
						if (sc.sharewordsRatio(v1, v2) > 0.8) {
							sameitems++;
						}
					}
				}
				f[5] = sameitems;
				f[6] = sameitems * 1.0 / Math.min(ibvitems, fbvitems);
				f[7] = sameitems * 1.0 / Math.max(ibvitems, fbvitems);
				f[8] = sameitems * 1.0 / Math.sqrt(ibvitems * fbvitems * 1.0);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		double sum = 0;
		for (double t : f) {
			sum += t;
		}
		return sum;
	}

	public static int compareIblist2Fblist(List<NameValues> ibvs,
			List<NameValues> fbvs, String guid, OutputStreamWriter osw) {
		int numMatch = 0;
		try {
			StringBuffer sb = new StringBuffer();
			// sort ibvs by the length of value_longstr, if fbvs[i] is 5 times
			// longer than ibvs[i].value_longstr, no need to compare them
			Collections.sort(ibvs);
			Collections.sort(fbvs);

			for (int i = 0; i < ibvs.size(); i++) {
				for (int j = 0; j < fbvs.size(); j++) {
					NameValues nvib = ibvs.get(i);
					NameValues nvfb = fbvs.get(j);
					if (nvfb.value_longstr.length() > 5 * nvib.value_longstr
							.length()) {
						break; // no need to compare them
					}
					double f[] = new double[FN];
					double sum = compareIbNv2FbNv(nvib, nvfb, f); // sum is just
					// to see if
					// they are
					// unrelated
					// at all
					if (sum > 0) {
						if (f[1] <= 2 && f[3] < 0.15) {// coincident match
							continue;
						}
						sb.append(guid + "\t" + nvib.name + "\t" + nvfb.name
						// + "\t" + nvib.values + "\t" + nvfb.values
								+ "\t");
						// System.out.print(nvib.name + "\t"+ nvfb.name+"\t");
						for (double a : f) {
							sb.append(a + "\t");
							// System.out.print(a+"\t");
						}
						sb.append("\n");
						numMatch++;
						// System.out.println();
					}

				}
			}
			osw.write(sb.toString());
		} catch (Exception e) {
			e.printStackTrace();
		}
		return numMatch;

	}

	private static void debug(List<KeyLine> klatts, List<KeyLine> klprops) {

		for (KeyLine klatt : klatts) {
			System.out.print(klatt.key + "; ");
		}
		System.out.println();
		for (KeyLine klprop : klprops) {
			System.out.print(klprop.key + "; ");
		}
		System.out.println();
	}

}
